package com.ab.http1;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.ObjectOutputStream.PutField;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.http.HttpEntity;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;


/**
 * A collection of string request parameters or files to send along with
 * requests made from an {@link AsyncHttpClient} instance.
 * <p>
 * For example:
 * <p>
 * <pre>
 * RequestParams params = new RequestParams();
 * params.put("username", "james");
 * params.put("password", "123456");
 * params.put("email", "my&#064;email.com");
 * params.put("profile_picture", new File("pic.jpg")); // Upload a File
 * params.put("profile_picture2", someInputStream); // Upload an InputStream
 * params.put("profile_picture3", new ByteArrayInputStream(someBytes)); // Upload some bytes
 *
 * AsyncHttpClient client = new AsyncHttpClient();
 * client.post("http://myendpoint.com", params, responseHandler);
 * </pre>
 */
public class RequestParams {

	private static String ENCODING="UTF-8";
	
	protected ConcurrentHashMap<String , String> urlParams;
	protected ConcurrentHashMap<String, FileWrapper> fileParams;
	
	/**
     * Constructs a new empty <code>RequestParams</code> instance.
     */
	public RequestParams(){
		init();
	}
	
	/**
     * Constructs a new RequestParams instance containing the key/value
     * string params from the specified map.
     * @param source the source key/value string map to add.
     */
	public RequestParams(Map<String, String> source){
		init();
		
		for(Map.Entry<String, String> entry :source.entrySet()){
			
		}
	}
	
	 /**
     * Constructs a new RequestParams instance and populate it with a single
     * initial key/value string param.
     * @param key the key name for the intial param.
     * @param value the value string for the initial param.
     */
	public RequestParams(String key, String value){
		init();
	
		put(key, value);
	}
	
	 /**
     * Adds a key/value string pair to the request.
     * @param key the key name for the new param.
     * @param value the value string for the new param.
     */
	public void put(String key, String value){
		if(key!=null && value!=null){
			urlParams.put(key, value);
		}
	}
	
	 /**
     * Adds a file to the request.
     * @param key the key name for the new param.
     * @param file the file to add.
     */
	public void put(String key, File file) throws FileNotFoundException{
		put(key, new FileInputStream(file), file.getName());
	}
	
	 /**
     * Adds an input stream to the request.
     * @param key the key name for the new param.
     * @param stream the input stream to add.
     */
	public void put(String key, InputStream stream){
		put(key, stream, null);
	}
	
	/**
     * Adds an input stream to the request.
     * @param key the key name for the new param.
     * @param stream the input stream to add.
     * @param fileName the name of the file.
     */
	public void put(String key, InputStream stream, String fileName){
		put(key, stream, fileName, null);
	}
	
	/**
     * Adds an input stream to the request.
     * @param key the key name for the new param.
     * @param stream the input stream to add.
     * @param fileName the name of the file.
     * @param contentType the content type of the file, eg. application/json
     */
	public void put(String key, InputStream stream, String fileName, String contentType){
		if(key!=null && stream!=null){
			fileParams.put(key, new FileWrapper(stream, fileName, contentType));
		}
	}
	
	/**
     * Removes a parameter from the request.
     * @param key the key name for the parameter to remove.
     */
	public void remove(String key){
		urlParams.remove(key);
		fileParams.remove(key);
	}
	
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		StringBuilder result=new StringBuilder();
		for(ConcurrentHashMap.Entry<String, String> entry : urlParams.entrySet()){
			if(result.length()>0){
				result.append("&");
			}
			
			result.append(entry.getKey());
			result.append("=");
			result.append(entry.getValue());
		}
		
		for(ConcurrentHashMap.Entry<String, FileWrapper> entry : fileParams.entrySet()){
			if(result.length()>0){
				result.append("&");
			}
			
			result.append(entry.getKey());
			result.append("=");
			result.append("FILE");
		}
		
		return result.toString();
	}
	
	HttpEntity getEntity(){
		HttpEntity entity=null;
		
		if(!fileParams.isEmpty()){
			SimpleMultipartEntity multipartEntity=new SimpleMultipartEntity();
			
			//add string params
			for(ConcurrentHashMap.Entry<String, String> entry : urlParams.entrySet()){
				multipartEntity.addPart(entry.getKey(), entry.getValue());
			}
			
			//add file params
			int currentIndex=0;
			int lastIndex=fileParams.entrySet().size()-1;
			for(ConcurrentHashMap.Entry<String, FileWrapper> entry : fileParams.entrySet()){
				FileWrapper file = entry.getValue();
				if(file.inputStream!=null){
					boolean isLast=currentIndex==lastIndex;
					if(file.contentType!=null){
						multipartEntity.addPart(entry.getKey(), file.getFileName(), file.inputStream, file.contentType, isLast);
					}else{
						multipartEntity.addPart(entry.getKey(), file.getFileName(), file.inputStream, isLast);
					}
				}
				currentIndex++;
			}
			
			entity=multipartEntity;
		}else{
			try {
				entity=new UrlEncodedFormEntity(getParamsList(), ENCODING);
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
		return entity;
	}
	
	private void init(){
		urlParams=new ConcurrentHashMap<String, String>();
		fileParams=new ConcurrentHashMap<String, FileWrapper>();
	}


	protected List<BasicNameValuePair> getParamsList(){
		List<BasicNameValuePair> lparams=new LinkedList<BasicNameValuePair>();
		for(ConcurrentHashMap.Entry<String, String> entry : urlParams.entrySet()){
			lparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
		}
		
		return lparams;
	}
	
	protected String getParamsString(){
		return URLEncodedUtils.format(getParamsList(), ENCODING);
	}
	

	private static class FileWrapper {
		public InputStream inputStream;
		public String fileName;
		public String contentType;
		
		public FileWrapper(InputStream inputStream, String fileName, String contentType){
			this.inputStream=inputStream;
			this.fileName=fileName;
			this.contentType=contentType;
		}
		
		public String getFileName(){
			if(fileName!=null)
				return fileName;
			else {
				return "noFileName";
			}
		}
	}
}
